// Copyright 2022 jeff.li. and/or its affiliates.

#include <tbir/runtime/container/itertor_ref.h>

#include <tbir/runtime/container/_ft_object_base.h>
#include <tbir/runtime/container/itertor_private.h>
#include <tbir/runtime/generic/generic_constructor_funcs.h>
#include <tbir/runtime/registry.h>

namespace tbir::runtime {

    Iterator Iterator::MakeGenericIterator(RTValue container,
                                           std::function<bool()> has_next,
                                           std::function<RTValue()> next,
                                           std::function<RTValue(bool *)> next_and_check) {
        auto data = make_object<GenericIteratorNode>(
                std::move(container), std::move(has_next), std::move(next), std::move(next_and_check));
        return Iterator(std::move(data));
    }

    Iterator Iterator::MakeGenericIterator(const Any &container) {
        return Kernel_Iterable::make(container);
    }

    Iterator Iterator::MakeItemsIterator(const Any &container) {
        if (container.type_code() == TypeIndex::kRuntimeDict) {
            return container.AsObjectViewNoCheck<Dict>().data().item_iter();
        } else if (container.type_code() == TypeIndex::kRuntimeFTDict) {
            return container.AsObjectViewNoCheck<FTObjectBase>()
                    .data()
                    .generic_call_attr("items", {})
                    .As<Iterator>();
        } else {
            return Kernel_Iterable::make(container);
        }
    }

    bool Iterator::all_items_equal(const Iterator &lhs, const Iterator &rhs) {
        // maybe we need a deep copy
        auto lhs_node = lhs.GetMutableNode();
        auto rhs_node = rhs.GetMutableNode();
        if (lhs_node == rhs_node) {
            return true;
        }
        bool has_next_l = lhs_node->HasNext();
        bool has_next_r = rhs_node->HasNext();
        if (has_next_l != has_next_r) {
            return false;
        }
        while (has_next_l && has_next_r) {
            auto lhs_v = lhs_node->Next(&has_next_l);
            auto rhs_v = rhs_node->Next(&has_next_r);
            if (lhs_v != rhs_v) {
                return false;
            }
        }
        if (has_next_l != has_next_r) {
            return false;
        }
        return true;
    }

    template<>
    bool IsConvertible<Iterator>(const Object *node) {
        return node ? node->IsInstance<Iterator::ContainerType>() : Iterator::_type_is_nullable;
    }

    IteratorNode *Iterator::GetMutableNode() const {
        MX_DPTR(Iterator);
        return d;
    }

    bool Iterator::HasNext() const {
        MX_DPTR(Iterator);
        return d != nullptr && d->HasNext();
    }

    RTValue Iterator::Next() const {
        MX_DPTR(Iterator);
        return d ? d->Next() : None;
    }

    RTValue Iterator::Next(bool *has_next) const {
        MX_DPTR(Iterator);
        return d ? d->Next(has_next) : None;
    }

    RTView Iterator::NextView(bool *has_next, RTValue *holder_or_null) const {
        MX_DPTR(Iterator);
        return d ? d->NextView(has_next, holder_or_null) : RTView();
    }

    int64_t Iterator::Distance() const {
        MX_DPTR(Iterator);
        return d ? d->Distance() : 0;
    }

    std::ostream &operator<<(std::ostream &os, Iterator const &n) {
        // TODO: GetRepr from Node
        os << "Iterator";
        return os;
    }

}  // namespace tbir::runtime
